/**
 * @description: 通用的AJAX请求封装，
 * @return 使用方式 import Request, { $request, $get, $post, $put, $delete } from '@/utils/request';
 */
import axios from 'axios';
import { ElMessage } from 'hive-plus';
import Qs from 'qs';

import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError, InternalAxiosRequestConfig, AxiosHeaders } from 'axios';

import { requestStore } from '@/store/request';
import { LocalStorageService, SessionStorageService } from '@/utils/storage';

const {
  DEV: isDev,
  VITE_APP_PROXY_GATEWAY: proxyKey,
  VITE_APP_BASE_API: baseApi,
  VITE_APP_GAIA_API_KEY: apiKey,
} = import.meta.env;

const clearStorage = () => {
  LocalStorageService.clear();
  SessionStorageService.clear();
};
let myRequestStore: any;

/**
 * @description: 需要主动调用new Request(), 可传递更多配置参数进去
 * @return {*}
 */
export class Request {
  instance: AxiosInstance;

  baseConfig: AxiosRequestConfig = {
    headers: {
      'X-Gaia-Api-Key': apiKey,
    },
    baseURL: isDev ? proxyKey : baseApi,
    timeout: 30000,
    withCredentials: true,
  };

  constructor(axiosConfig?: AxiosRequestConfig) {
    this.instance = axios.create(Object.assign(this.baseConfig, axiosConfig));

    this.instance.interceptors.request.use(
      (requestConfig: InternalAxiosRequestConfig) => {
        if (!myRequestStore) {
          myRequestStore = requestStore();
        }
        const controller = new AbortController(); // 每个请求时都新生成一个AbortController实例
        requestConfig.signal = controller.signal; // 设置请求的signal字段为new AbortController()的signal
        myRequestStore.addRequest(controller);

        // 这里一个token，你需要在这里取到你设置好的token的值 示例逻辑代码
        const token = LocalStorageService.get('_user_info')?.sessionId || '';
        if (token) {
          // 这里将token设置到headers中，header的key是Authorization，这个key值根据你的需要进行修改即可
          requestConfig.headers = {
            sessionId: token,
            ...requestConfig.headers,
          } as unknown as AxiosHeaders;
        }

        requestConfig.paramsSerializer = (params) => Qs.stringify(params, { arrayFormat: 'repeat' });

        return requestConfig;
      },
      (error: any) => Promise.reject(error)
    );

    this.instance.interceptors.response.use(
      (response: AxiosResponse) => {
        const requestUrl = response.config.url || '';
        const { headers, data: result } = response;

        // 后端直接返回文件流
        if (!headers['content-type']?.includes('application/json') || result instanceof Blob) {
          return response;
        }
        // 返回数据的code 不为200即为数据出错
        if (+result.code === 0) {
          return result;
        }
        if (result.code === 401) {
          clearStorage();
          if (result.loginUrl) {
            window.location.href = result.loginUrl || '';
          } else {
            ElMessage({
              message: `${requestUrl} ${result.msg || '需要重新登录'}`,
              type: 'error',
              grouping: true,
            });
            throw new Error(`${result.loginUrl} 需要重新登录`);
          }
        }
        if (result.code === 403) {
          ElMessage({
            message: `${requestUrl} 暂无访问权限`,
            type: 'error',
            grouping: true,
          });
          throw new Error(`${requestUrl} 暂无访问权限`);
        }

        ElMessage({
          message: result.message || result.msg || 'Error',
          type: 'error',
          grouping: true,
        });
        throw new Error(result.message || result.msg || 'Error'); // 预期结果失败
      },
      (err: AxiosError) => {
        const requestUrl = err?.config?.url || '';
        if (err.toString().includes('timeout')) {
          ElMessage({
            message: '请求超时，请稍后再试',
            type: 'error',
            grouping: true,
          });
        }
        if (err && err.response) {
          const { status, data, config }: any = err.response;
          switch (status) {
          case 400:
            err.message = '请求错误';
            break;

          case 401:
            // 用户登录信息认证失败，退出到登录页之前清空缓存内容
            clearStorage();

            location.href = data.loginUrl;
            break;

          case 403:
            err.message = `${requestUrl} 暂无访问权限`;
            break;

          case 404:
            err.message = `请求地址出错: ${config.url}`;
            break;

          case 408:
            err.message = '请求超时';
            break;

          case 500:
          case 501:
          case 502:
          case 503:
          case 504:
            err.message = '服务器开个小差，请稍后再试';
            break;

          case 505:
            err.message = 'HTTP版本不受支持';
            break;

          default:
            err.message = '请求失败，请稍后再试';
          }
          ElMessage({
            message: err.message,
            type: 'error',
            grouping: true,
          });
        }
        return Promise.reject(err);
      }
    );
  }

  public request<T = any>(config: AxiosRequestConfig): Promise<T> {
    return this.instance.request(config);
  }

  public get<T = any>(
    url: string,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.instance.get(url, config);
  }

  public post<T = any>(
    url: string,
    data?: TypeObject,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.instance.post(url, data, config);
  }

  public put<T = any>(
    url: string,
    data?: TypeObject,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.instance.put(url, data, config);
  }

  public delete<T = any>(
    url: string,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.instance.delete(url, config);
  }
}

/**
 * @description: 封装默认配置的axios的request、get、post、put、delete
 * @return {*}
 */
const server = new Request();
const $request = <T = any>(config: AxiosRequestConfig): Promise<T> => server.request(config);

const $get = <T = any>(
  url: string,
  config?: AxiosRequestConfig
): Promise<T> => server.get(url, config);

const $post = <T = any>(
  url: string,
  data?: TypeObject,
  config?: AxiosRequestConfig
): Promise<T> => server.post<T>(url, data, config);

const $put = <T = any>(
  url: string,
  data?: TypeObject,
  config?: AxiosRequestConfig
): Promise<T> => server.put<T>(url, data, config);

const $delete = <T = any>(
  url: string,
  config?: AxiosRequestConfig
): Promise<T> => server.delete<T>(url, config);

export {
  $request,
  $get,
  $post,
  $put,
  $delete,
};
export default server;
